import java.util.ArrayList;
import java.util.Comparator;
import java.util.Map;

import net.metalvz.linq.Enumerable;
import net.metalvz.linq.IEnumerable;
import net.metalvz.linq.Func1;
import net.metalvz.linq.IGrouping;
import net.metalvz.linq.Predicate;

public class TestClass {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		testCount();
		testGroupBy();
		testOrderBy();
		testSelect();
		testTakeAndSkip();
		testToMap();
		testWhere();
	}
	
	private static void testCount() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] count():");
		System.out.println("=================");
		
		Integer[] array = new Integer[] { 23979, 5979, 1, 2 }; 
		IEnumerable<Integer> arraySeq = Enumerable.toEnumerable(array);
		
		ArrayList<String> list = new ArrayList<String>();
		list.add("MK");
		list.add("TM");		
		IEnumerable<String> listSeq = Enumerable.asEnumerable(list);
		
		IEnumerable<String> filteredListSeq = listSeq.where(new Predicate<String>() {
			@Override
			public boolean invoke(String obj) {
				return "TM".equals(obj);
			}
		});
		
		System.out.println(String.format("Count array: %s", arraySeq.count()));
		System.out.println(String.format("Count list: %s", listSeq.count()));
		System.out.println(String.format("Count filtered (item == 'TM'): %s", filteredListSeq.count()));
	}
	
	private static void testGroupBy() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] groupBy():");
		System.out.println("=================");		
		
		Integer[] array = new Integer[] { 1 , 2, 3, 4, 5, 6, 7, 8, 9 };
		IEnumerable<Integer> arraySeq = Enumerable.toEnumerable(array);
		
		IEnumerable<IGrouping<Integer, Integer>> groupedSeq =
		    arraySeq.groupBy(new Func1<Integer, Integer>() {
				@Override
				public Integer invoke(Integer arg) {
					return arg % 3;
				}		    	
			});
		
		for (IGrouping<Integer, Integer> grp: groupedSeq) {
			System.out.println(String.format("x %% 3 == %s", grp.key()));
			for (Integer item: grp) {
				System.out.println(String.format("    %s", item));
			}
		}
	}
	
	private static void testOrderBy() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] orderBy():");
		System.out.println("=====================");
		
		Integer[] array = new Integer[] { 1, 20, 3, 2, 30, 29 }; 
		IEnumerable<Integer> intSeq = Enumerable.toEnumerable(array);
		
		System.out.println("source:");
		for (Integer i: intSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> sortedSeq = intSeq.orderBy(new Comparator<Integer>() {			
			@Override
			public int compare(Integer x, Integer y) {
				return x.compareTo(y);
			}
		});
		
		System.out.println("");
		System.out.println("sortedSeq:");
		for (Integer i: sortedSeq) {
			System.out.println(i);
		}
	}
	
	private static void testSelect() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] select():");
		System.out.println("=====================");
		
		Integer[] array = new Integer[] { 23979, 5979, 1, 2, null }; 
		IEnumerable<Integer> intSeq = Enumerable.toEnumerable(array);
		
		System.out.println("source:");
		for (Integer i: intSeq) {
			System.out.println(String.format("%s (%s)",
						                     i,
						                     i != null ? i.getClass().getName() : "(null)"));
		}
		
		IEnumerable<String> strSeq = intSeq.select(new Func1<Integer, String>() {
			@Override
			public String invoke(Integer arg) {
				return arg != null ? arg.toString() : null;
			}
		});
		
		System.out.println("");
		System.out.println("converted source (Integer => String):");
		for (String str: strSeq) {
			System.out.println(String.format("%s (%s)",
							   str,
							   str != null ? str.getClass().getName() : "(null)"));
		}
		
		IEnumerable<Long> longSeq = intSeq.selectMany(new Func1<Integer, Iterable<Long>>() {
			@Override
			public Iterable<Long> invoke(Integer arg) {
				return Enumerable.toEnumerable(new Long[]
				{
				    arg != null ? Long.valueOf(arg.toString()) : null,
				    arg != null ? Long.valueOf(arg.toString()) * 3 : null,
				});
			}
		});
		
		System.out.println("");
		System.out.println("converted source by selectMany() (Integer => Long):");
		for (Long l: longSeq) {
			System.out.println(String.format("%s (%s)",
							   l,
							   l != null ? l.getClass().getName() : "(null)"));
		}
		
		IEnumerable<Long> longSeqArray = intSeq.selectManyArray(new Func1<Integer, Long[]>() {
			@Override
			public Long[] invoke(Integer arg) {
				return new Long[]
				{
				    arg != null ? Long.valueOf(arg.toString()) : null,
				    arg != null ? Long.valueOf(arg.toString()) * 10 : null,
				};
			}
		});
		
		System.out.println("");
		System.out.println("converted source by selectManyArray() (Integer => Long):");
		for (Long l: longSeqArray) {
			System.out.println(String.format("%s (%s)",
							   l,
							   l != null ? l.getClass().getName() : "(null)"));
		}
	}
	
	private static void testTakeAndSkip() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] take / skip:");
		System.out.println("========================");
		
		Integer[] array = new Integer[] { 23979, 5979, 1, 2, 666 }; 
		IEnumerable<Integer> sequence = Enumerable.toEnumerable(array);
		
		System.out.println("original:");
		for (Integer i: sequence) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> takeSeq = sequence.take(2);
		System.out.println("");
		System.out.println("take(2):");
		for (Integer i: takeSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> skipSeq = sequence.skip(3);
		System.out.println("");
		System.out.println("skip(3):");
		for (Integer i: skipSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> skipAndTakeSeq = sequence.skip(1).take(3);
		System.out.println("");
		System.out.println("skip(1)+take(3):");
		for (Integer i: skipAndTakeSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> takeAndSkipSeq = sequence.take(3).skip(2);
		System.out.println("");
		System.out.println("take(3)+skip(2):");
		for (Integer i: takeAndSkipSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> skipWhileSeq = sequence.skipWhile(new Predicate<Integer>() {
			@Override
			public boolean invoke(Integer obj) {
				return obj > 2;
			}			
		});
		System.out.println("");
		System.out.println("skipWhile(x > 2):");
		for (Integer i: skipWhileSeq) {
			System.out.println(i);
		}
		
		IEnumerable<Integer> takeWhileSeq = sequence.takeWhile(new Predicate<Integer>() {
			@Override
			public boolean invoke(Integer obj) {
				return obj > 1;
			}			
		});
		System.out.println("");
		System.out.println("takeWhile(x > 1):");
		for (Integer i: takeWhileSeq) {
			System.out.println(i);
		}
	}
	
	private static void testToMap() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] toMap():");
		System.out.println("====================");
		
		Integer[] array = new Integer[] { 1, 20, 3, 2, 30, 29 }; 
		IEnumerable<Integer> intSeq = Enumerable.toEnumerable(array);
		
		try {
			System.out.println("NO duplicate keys:");
			Map<String, Integer> map = intSeq.toMap(new Func1<Integer, String>() {
				@Override
				public String invoke(Integer arg) {
					return "KEY_" + arg.toString();
				}
			});
			
			for (Map.Entry<String, Integer> entry: map.entrySet()) {
				System.out.println(String.format("%s => %s",
						                         entry.getKey(),
						                         entry.getValue()));
			}
		}
		catch (Throwable ex) {
			ex.printStackTrace();
		}
		
		try {
			System.out.println("");
			System.out.println("duplicate keys:");
			Map<String, Integer> map = intSeq.toMap(new Func1<Integer, String>() {
				@Override
				public String invoke(Integer arg) {
					Integer key = arg % 2;
					return "KEY_" + key.toString();
				}
			});
			
			// this should not be executed
			for (Map.Entry<String, Integer> entry: map.entrySet()) {
				System.out.println(String.format("%s => %s",
						                         entry.getKey(),
						                         entry.getValue()));
			}			
		}
		catch (Throwable ex) {
			ex.printStackTrace();
		}
	}
	
	private static void testWhere() {
		System.out.println("");
		System.out.println("");
		System.out.println("[TEST] where():");
		System.out.println("=================");
		
		Integer[] array = new Integer[] { 23979, 5979, 1, 2 }; 
		IEnumerable<Integer> sequence = Enumerable.toEnumerable(array);
		
		System.out.println("un-filtered:");
		for (Integer i: sequence) {
			System.out.println(i);
		}
		
		System.out.println("");
		System.out.println("filtered (item > 2):");
		IEnumerable<Integer> filteredSeq = sequence.where(new Predicate<Integer>() {															
		    @Override
		    public boolean invoke(Integer obj) {
			    return obj > 2;
		    }
	     });
		
		for (Integer i: filteredSeq) {
			System.out.println(i);
		}
	}
}
